#define GLEW_STATIC // Easier debugging
#include <GL/glew.h>
#include <GL/gl.h>
//#include <windows.h>  // For MS Windows
//#include <GL/glut.h>  // GLUT, includes glu.h and gl.h For MS Windows
#include <SDL/SDL.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>

#include "GameAsset.h"
#include "Md2Asset.h"
#include "CubeAsset.h"
#include "PlaneAsset.h"
#include "BallisticInterpolator.h"
#include "Camera.h"
#include "UserInput.h"

using namespace std;

#define RUN_GRAPHICS_DISPLAY 0x00;

string filename = "data/ogre.md2";
vector<shared_ptr<GameAsset> > assets;

/*
 * SDL timers run in separate threads.  In the timer thread
 * push an event onto the event queue.  This event signifies
 * to call display() from the thread in which the OpenGL 
 * context was created.
 */

/*
 * If you are devving on Windows, put the DLLs in the DLL folder into the same folder
 * as your executable binary file.
 */

Uint32 display(Uint32 interval, void *param) {
	SDL_Event event;
	event.type = SDL_USEREVENT;
	event.user.code = RUN_GRAPHICS_DISPLAY;
	event.user.data1 = 0;
	event.user.data2 = 0;
	SDL_PushEvent(&event);
	return interval;
}

bool i_pressed, j_pressed, k_pressed, l_pressed;
bool y_pressed, h_pressed;
CubeAsset *cube4;
bool camera_move = false;
bool debug_mode = true;
bool rot_up, rot_left, rot_down, rot_right;
bool up_pressed, left_pressed, down_pressed, right_pressed;
bool w_pressed, a_pressed, s_pressed, d_pressed, q_pressed, e_pressed;
bool space_pressed, lshift_pressed;
bool wheel_up, wheel_down;

bool g_pressed, t_pressed, b_pressed;

CubeAsset *bullet1, *bullet2, *bullet3;
bool bullet1fired, bullet2fired, bullet3fired;

CubeAsset *cube1, *cube2, *cube3, *cube5;

Matrix4 camera;
GLfloat move_speed = 0.2;


//Makes the bullets move forward if they have been fired.
void move_bullets(){
	if(bullet1fired){
		bullet1->move(0);
	}else{
		bullet1->mv_matrix = cube4->mv_matrix;
	}
	if(bullet2fired){
		bullet2->move(0);
	}else{
		bullet2->mv_matrix = cube4->mv_matrix;
	}
	if(bullet3fired){
		bullet3->move(0);
	}else{
		bullet3->mv_matrix = cube4->mv_matrix;
	}
}

//move the enemies down the lane
void move_enemies(){
	cube1->move(2);
	cube2->move(2);
	cube3->move(2);
	cube5->move(2);
}

void display() {
	move_bullets();
	move_enemies();
	  //move cube forward
	  if(i_pressed){
		  if(debug_mode){
			  cube4->move(0);
		  }
	  }
	  //move cube left
	  if(j_pressed){
		  if(debug_mode){
			  cube4->move(1);
		  }
	  }
	  //move cube backward
	  if(k_pressed){
		  if(debug_mode){
			  cube4->move(2);
		  }
	  }
	  //move cube right
	  if(l_pressed){
		  if(debug_mode){
			  cube4->move(3);
		  }
	  }
	  //move cube up
	  if(y_pressed){
		  if(debug_mode){
			  cube4->move(4);
		  }
	  }
	  //move cube down
	  if(h_pressed){
		  if(debug_mode){
			  cube4->move(5);
		  }
	  }

	  //rot cam up
	  if(w_pressed){
		  if(debug_mode){
			  Camera::getInstance().rotate(0);
		  }else{
			  //move cube forward
			  cube4->move(0);
			  Camera::getInstance().move(5);
		  }
	  }
	  //rot cam left
	  if(a_pressed){
		  if(debug_mode){
			  Camera::getInstance().rotate(1);
		  }else{
			  //move cube left
			  cube4->move(1);
			  Camera::getInstance().move(1);
		  }
	  }
	  //rot cam down
	  if(s_pressed){
		  if(debug_mode){
			  Camera::getInstance().rotate(2);
		  }else{
			  //move cube backward
			  cube4->move(2);
			  Camera::getInstance().move(4);
		  }
	  }
	  //rot cam right
	  if(d_pressed){
		  if(debug_mode){
			  Camera::getInstance().rotate(3);
		  }else{
			  //move cube right
			  cube4->move(3);
			  Camera::getInstance().move(3);
		  }
	  }
	  //roll cam cw
	  if(e_pressed){
		  if(debug_mode){
			  Camera::getInstance().rotate(4);
		  }
	  }
	  //roll cam ccw
	  if(q_pressed){
		  if(debug_mode){
			  Camera::getInstance().rotate(5);
		  }
	  }

	  //move cam forward
	  if(up_pressed){
		  if(debug_mode){
			  Camera::getInstance().move(0);
		  }
	  }
	  //move cam left
	  if(left_pressed){
		  if(debug_mode){
			  Camera::getInstance().move(1);
		  }
	  }
	  //move cam backward
	  if(down_pressed){
		  if(debug_mode){
			  Camera::getInstance().move(2);
		  }else{

		  }
	  }
	  //move cam right
	  if(right_pressed){
		  if(debug_mode){
			  Camera::getInstance().move(3);
		  }
	  }
	  //move cam up
	  if(lshift_pressed){
		  if(debug_mode){
			  Camera::getInstance().move(4);
		  }
	  }
	  //move cam down
	  if(space_pressed){
		  if(debug_mode){
			  Camera::getInstance().move(5);
		  }
	  }


	  if(t_pressed){

	  }
	  if(b_pressed){

	  }

	  if(g_pressed){

	  }

	  if(wheel_up){

	  }

	  if(wheel_down){

	  }

  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  for(int i=0; i<assets.size(); i++){
	  assets.at(i)->update();
  }

  for(int i=0; i<assets.size(); i++){
	  for(int j=0; j<assets.size(); j++){
		  if((assets.at(i) != assets.at(j)) && assets.at(i)->collidesWith(*assets.at(j))) {
				  cout << "We have a collision"  << endl;
				  //Comment this out if you want to see the red(enemy) cubes
				  //moves the cubes in separate locations
				  cube1->move(1);
				  cube2->move(2);
				  cube3->move(3);
				  cube5->move(0);
		  }
	  }
  }

  for(int i=0; i<assets.size(); i++){
	  assets.at(i)->draw();
  }
  
  // Don't forget to swap the buffers
  SDL_GL_SwapBuffers();
}

int main(int argc, char ** argv) {
	SDL_Surface * surf;
	Uint32 width = 640;
	Uint32 height = 480;
	Uint32 colour_depth = 16; // in bits
	Uint32 delay = 1000/60; // in milliseconds

	// Initialise SDL - when using C/C++ it's common to have to
	// initialise libraries by calling a function within them.
	if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER)<0) {
			cout << "Failed to initialise SDL: " << SDL_GetError() << endl;
			SDL_Quit();
	}

	// When we close a window quit the SDL application
	atexit(SDL_Quit);

	// Create a new window with an OpenGL surface
	if (!(surf = SDL_SetVideoMode(width, height, colour_depth, SDL_OPENGL))) {
			cout << "Failed to initialise video mode: " << SDL_GetError() << endl;
			SDL_Quit();
	}

	// Initialise GLEW - an easy way to ensure OpenGl 2.0+
	// The *must* be done after we have set the video mode - otherwise we have no OpenGL context.
	glewInit();
	//allows for proper rendering, rather than first come first serve
	glEnable(GL_DEPTH_TEST);

	//Makes the objects render their wireframe, rather than flatly
	glEnable(GL_LIGHTING);
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	//initiate all the assets
	PlaneAsset *ground = new PlaneAsset(1.5, 1.5, 0, "shaders/hello-gl3.f.glsl");
	ground->scale(10.0, 10.0, 10.0);
	cube1 = new CubeAsset(0, 30, 0, "shaders/hello-gl.f.glsl");
	cube2 = new CubeAsset(1, 30, 0, "shaders/hello-gl.f.glsl");
	cube3 = new CubeAsset(2, 30, 0, "shaders/hello-gl.f.glsl");
	cube4 = new CubeAsset(0, 0, 0, "shaders/hello-gl2.f.glsl");
	cube5 = new CubeAsset(3, 30, 0, "shaders/hello-gl.f.glsl");

	shared_ptr<GameAsset> ground_asset = shared_ptr<GameAsset> (ground);
	ground_asset->rename("Ground");
	assets.push_back(ground_asset);

	shared_ptr<GameAsset> asset1 = shared_ptr<GameAsset> (cube1);
	asset1->rename("Enemy");
	assets.push_back(asset1);

	shared_ptr<GameAsset> asset2 = shared_ptr<GameAsset> (cube2);
	asset2->rename("Enemy");
	assets.push_back(asset2);

	shared_ptr<GameAsset> asset3 = shared_ptr<GameAsset> (cube3);
	asset3->rename("Enemy");
	assets.push_back(asset3);

	shared_ptr<GameAsset> asset4 = shared_ptr<GameAsset> (cube4);
	asset4->rename("Player");
	assets.push_back(asset4);

	shared_ptr<GameAsset> asset5 = shared_ptr<GameAsset> (cube5);
	asset5->rename("Enemy");
	assets.push_back(asset5);

	  bullet1 = new CubeAsset(0, 0, 0, "shaders/bullet1.f.glsl");
	  shared_ptr<GameAsset> bullet_asset1 = shared_ptr<GameAsset> (bullet1);
	  bullet_asset1->rename("Bullet");
	  assets.push_back(bullet_asset1);

	  bullet2 = new CubeAsset(0, 0, 0, "shaders/bullet2.f.glsl");
	  shared_ptr<GameAsset> bullet_asset2 = shared_ptr<GameAsset> (bullet2);
	  bullet_asset2->rename("Bullet");
	  assets.push_back(bullet_asset2);

	  bullet3 = new CubeAsset(0, 0, 0, "shaders/bullet3.f.glsl");
	  shared_ptr<GameAsset> bullet_asset3 = shared_ptr<GameAsset> (bullet3);
	  bullet_asset3->rename("Bullet");
	  assets.push_back(bullet_asset3);

	// Call the function "display" every delay milliseconds
	SDL_AddTimer(delay, display, NULL);

	// Add the main event loop
	SDL_Event event;
	camera = Camera::getInstance().getCameraM();

	//Hide the cursor from view, if needed
	SDL_ShowCursor(0);
	//Throw the mouse to the center of the window
	SDL_WarpMouse(width/2, height/2);

	//still not functional, may be scrapped
	//UserInput *input = new UserInput();
	//input->do_things();

	while (SDL_WaitEvent(&event)) {
		//Check for key events
		switch (event.type) {

			case SDL_MOUSEMOTION:
				//This deals with the various mouse motions, though none of them are working as they should
				//They have no been implemented
				Sint16 x_move, y_move;
				Sint16 x_ref, y_ref;
				x_ref = 0;
				y_ref = 0;
				x_move = event.motion.xrel;
				y_move = event.motion.yrel;
				//Mouse is not moving
				if(x_move == x_ref){
					rot_left = false;
					rot_right = false;
				}
				if(y_move == y_ref){
					rot_up = false;
					rot_down = false;
				}
				//Mouse moved up
				if(x_move > x_ref){
					rot_left = true;
				}
				//Mouse moved left
				if(x_move < x_ref){
					rot_right = true;
				}
				//Mouse moved down
				if(y_move > y_ref){
					rot_up = true;
				}
				//Mouse moved
				if(y_move < y_ref){
					rot_down = true;

				}
				break;
				break;

			case SDL_MOUSEBUTTONUP:
				switch(event.button.button){
					case SDL_BUTTON_LEFT:
						if(debug_mode){
							debug_mode = false;
						}else{
							debug_mode = true;
						}
						break;
					case SDL_BUTTON_RIGHT:

						break;
					case SDL_BUTTON_WHEELUP:
						wheel_up = false;
						break;
					case SDL_BUTTON_WHEELDOWN:
						wheel_down = false;
						break;
				}
				break;
			case SDL_MOUSEBUTTONDOWN:
				switch(event.button.button){
					case SDL_BUTTON_LEFT:
						break;

					case SDL_BUTTON_RIGHT:

						break;
					case SDL_BUTTON_WHEELUP:
						wheel_up = true;
						Camera::getInstance().move(0);
						break;
					case SDL_BUTTON_WHEELDOWN:
						wheel_down = true;
						Camera::getInstance().move(2);
						break;
				}
				break;

			case SDL_QUIT:
				SDL_Quit();
				break;

			case SDL_USEREVENT:
				display();
				break;

				//set all variables to false, upon releasing the keys
			case SDL_KEYUP:
				Camera::getInstance().setCamera(Matrix4::identity());
				switch(event.key.keysym.sym){
				case SDLK_i:
					i_pressed = false;
					break;
				case SDLK_j:
					j_pressed = false;
					break;
				case SDLK_k:
					k_pressed = false;
					break;
				case SDLK_l:
					l_pressed = false;
					break;
				case SDLK_y:
					y_pressed = false;
					break;
				case SDLK_h:
					h_pressed = false;
					break;

				case SDLK_w:
					w_pressed = false;
					break;
				case SDLK_a:
					a_pressed = false;
					break;
				case SDLK_s:
					s_pressed = false;
					break;
				case SDLK_d:
					d_pressed = false;
					break;
				case SDLK_LSHIFT:
					lshift_pressed = false;
					break;
				case SDLK_SPACE:
					space_pressed = false;
					break;
				case SDLK_q:
					q_pressed = false;
					break;
				case SDLK_e:
					e_pressed = false;
					break;

				case SDLK_g:
					g_pressed = false;
					break;
				case SDLK_t:
					t_pressed = false;
					break;
				case SDLK_b:
					b_pressed = false;
					break;

				case SDLK_UP:
					up_pressed = false;
					break;
				case SDLK_LEFT:
					left_pressed = false;
					break;
				case SDLK_DOWN:
					down_pressed = false;
					break;
				case SDLK_RIGHT:
					right_pressed = false;
					break;
				}

				break;

				//set all the variables to true, upon pressing the keys
			case SDL_KEYDOWN:
			  switch(event.key.keysym.sym){
			  	  case SDLK_c:
			  		rot_up = false;
					rot_left = false;
					rot_down = false;
					rot_right = false;
			  		break;

			  	  case SDLK_y:
			  		  y_pressed = true;
			  		  break;
				  case SDLK_h:
					  h_pressed = true;
					  break;
				  case SDLK_i:
					  i_pressed = true;
					  break;
				  case SDLK_j:
					  j_pressed = true;
					  break;
				  case SDLK_k:
					  k_pressed = true;
					  break;
				  case SDLK_l:
					  l_pressed = true;
					  break;

				  case SDLK_q:
					  q_pressed = true;
					  break;
				  case SDLK_e:
					  e_pressed = true;
					  break;

				  case SDLK_w:
					  w_pressed = true;
					  cout << "w pressed" << endl;
					  break;
				  case SDLK_a:
					  a_pressed = true;
					  break;
				  case SDLK_s:
					  s_pressed = true;
					  break;
				  case SDLK_d:
					  d_pressed = true;
					  break;
				  case SDLK_LEFT:
					  left_pressed = true;
					break;
				  case SDLK_RIGHT:
					  right_pressed = true;
					break;
				  case SDLK_UP:
					  up_pressed = true;
					break;
				  case SDLK_DOWN:
					  down_pressed = true;
					break;
					case SDLK_SPACE:
						space_pressed = true;
						if(debug_mode){

						}else{
							//Fire 3 bullets and cycles through them
							bool bullet1firedagain, bullet2firedagain, bullet3firedagain;

							if(bullet1fired
									&bullet2fired
									&bullet3fired){
								if(!bullet1firedagain){
									bullet1->mv_matrix = cube4->mv_matrix;
									bullet1->scale(0.2, 0.2, 0.2);
									bullet1firedagain = true;
									bullet3firedagain = false;
									break;
								}
								if(bullet1firedagain&
										!bullet2firedagain){
									bullet2->mv_matrix = cube4->mv_matrix;
									bullet2->scale(0.2, 0.2, 0.2);
									bullet1firedagain = false;
									bullet2firedagain = true;
									break;
								}
								if(bullet2firedagain&
										!bullet3firedagain){
									bullet3->mv_matrix = cube4->mv_matrix;
									bullet3->scale(0.2, 0.2, 0.2);
									bullet2firedagain = false;
									bullet3firedagain = true;
									break;
								}


							}
							if(bullet2fired&
									!bullet3fired){
								bullet3fired = true;
							}
							if(bullet1fired&
									!bullet2fired){
								bullet2fired = true;
							}
							if(!bullet1fired&
									!bullet2fired&
									!bullet3fired){
								bullet1fired = true;
							}
						}
					break;
					case SDLK_LSHIFT:
						lshift_pressed = true;
					break;

					case SDLK_g:
						g_pressed = true;
						if(debug_mode){
							//Fire 3 bullets and cycles through them
							bool bullet1firedagain, bullet2firedagain, bullet3firedagain;

							if(bullet1fired
									&bullet2fired
									&bullet3fired){
								if(!bullet1firedagain){
									bullet1->mv_matrix = cube4->mv_matrix;
									bullet1->scale(0.2, 0.2, 0.2);
									bullet1firedagain = true;
									bullet3firedagain = false;
									break;
								}
								if(bullet1firedagain&
										!bullet2firedagain){
									bullet2->mv_matrix = cube4->mv_matrix;
									bullet2->scale(0.2, 0.2, 0.2);
									bullet1firedagain = false;
									bullet2firedagain = true;
									break;
								}
								if(bullet2firedagain&
										!bullet3firedagain){
									bullet3->mv_matrix = cube4->mv_matrix;
									bullet3->scale(0.2, 0.2, 0.2);
									bullet2firedagain = false;
									bullet3firedagain = true;
									break;
								}


							}
							if(bullet2fired&
									!bullet3fired){
								bullet3fired = true;
							}
							if(bullet1fired&
									!bullet2fired){
								bullet2fired = true;
							}
							if(!bullet1fired&
									!bullet2fired&
									!bullet3fired){
								bullet1fired = true;
							}
						}
						break;
					case SDLK_t:
						t_pressed = true;
						break;
					case SDLK_b:
						b_pressed = true;
						break;

				  //Ends the program
				  case SDLK_ESCAPE:
					  SDL_Quit();
					  break;
				  default:
					break;
				  }
			  break;
			}
	}
	return 0;
}
